import { type Directive, ref, watch } from 'vue';
import { throttle } from '@peng_kai/kit/libs/lodash-es';

const rangeRE = /^(\d+)?\s*(<|<=)\s*\$\s*(<|<=)\s*(\d+)?$/;
const singleRE = /^\$\s*([<>]=?)\s*(\d+)$/;
const observedElements = new Map<HTMLElement, Set<(contentRect: DOMRectReadOnly) => void>>();
let sharedObserver: ResizeObserver | null = null;

function getSharedObserver() {
  if (!sharedObserver) {
    const callbackfn = throttle((entries: ResizeObserverEntry[]) => {
      for (const entry of entries) {
        const handlers = observedElements.get(entry.target as HTMLElement);
        if (handlers) {
          handlers.forEach(handler => handler(entry.contentRect));
        }
      }
    }, 200);
    sharedObserver = new ResizeObserver(callbackfn);
  }

  return sharedObserver;
}

function parseCondition(condition: string, width: number) {
  const parts = condition.split(',').map(part => part.trim());
  return parts.every((part) => {
    // 处理范围条件，包括所有变体
    const rangeMatch = part.match(rangeRE);
    if (rangeMatch) {
      const [, minStr, minOp, maxOp, maxStr] = rangeMatch;
      let result = true;
      if (minStr !== undefined) {
        const min = Number.parseFloat(minStr);
        result = result && (minOp === '<' ? width > min : width >= min);
      }
      if (maxStr !== undefined) {
        const max = Number.parseFloat(maxStr);
        result = result && (maxOp === '<' ? width < max : width <= max);
      }
      return result;
    }

    // 处理单侧条件，如 '$<=600', '$>=300'
    const singleMatch = part.match(singleRE);
    if (singleMatch) {
      const [, operator, value] = singleMatch;
      const numValue = Number.parseFloat(value);
      switch (operator) {
        case '<': return width < numValue;
        case '<=': return width <= numValue;
        case '>': return width > numValue;
        case '>=': return width >= numValue;
      }
    }

    return false; // 如果没有匹配任何条件，返回 false
  });
}

export const vContainerQuery: Directive = {
  mounted(el, binding) {
    const queries = binding.value;
    const state = ref<Record<string, boolean>>({});

    const handleResize = (contentRect: DOMRectReadOnly) => {
      const width = contentRect.width;
      for (const condition of Object.keys(queries)) {
        state.value[condition] = parseCondition(condition, width);
      }
    };

    const observer = getSharedObserver();
    observer.observe(el);

    if (!observedElements.has(el)) {
      observedElements.set(el, new Set());
    }
    observedElements.get(el)?.add(handleResize);

    const stopWatch = watch(state, (newState) => {
      const _el = el as HTMLElement;

      if (!_el)
        return;

      for (const [condition, isActive] of Object.entries(newState)) {
        const attrs = queries[condition];
        if (isActive) {
          if (attrs.class) {
            _el.className += ` ${attrs.class}`;
          }
          for (const [key, value] of Object.entries(attrs)) {
            if (key.startsWith('data-'))
              _el.setAttribute(key, String(value));
          }
        }
        else {
          if (attrs.class) {
            _el.className = _el.className.replace(attrs.class, '');
          }
          for (const key of Object.keys(attrs)) {
            if (key.startsWith('data-'))
              _el.removeAttribute(key);
          }
        }
      }
    }, { immediate: true, deep: true });

    el._containerQuery = { stopWatch, handleResize };
  },
  unmounted(el) {
    if (el._containerQuery) {
      el._containerQuery.stopWatch();
      const handlers = observedElements.get(el);
      if (handlers) {
        handlers.delete(el._containerQuery.handleResize);
        if (handlers.size === 0) {
          observedElements.delete(el);
          getSharedObserver().unobserve(el);
        }
      }
    }
  },
};
